//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __PPAGE_H__
#define __PPAGE_H__

class PPage : public DLinkNode
{
public:
    PPage * Clone();

    void MemZero();
    void MemCopy(void * pvBase);

public:
    UInt32            m_uOffset;  // page offset in data source
    UInt32            m_uAddr;    // physical base address
    UInt32            m_uSize;    // page size, align PAGE_SIZE
    UInt32            m_uOrder;   // 2^m_uOrder == m_uSize
};

inline void PPage::MemZero()
{
    memset(__VA(m_uAddr), 0, m_uSize);
}

inline void PPage::MemCopy(void *p)
{
    memcpy(__VA(m_uAddr), p, m_uSize);
}

#define PAGE_ASSERT(p)  \
    assert((1u << (p->m_uOrder + PAGE_SHIFT)) == p->m_uSize)

class PageContainer
{
public:
    PageContainer * Clone();

    ECode AllocPages(UInt32 uOffset, UInt32 cPages);
    PPage * CreatePage(UInt32 uOffset, UInt32 uOrder);
    void FreeAllPages();

    PPage * GetPage(UInt32 uOffset, Boolean bCreate);
    void PutPage(PPage * pPage);

    void MemZero();
    void MemCopy(void * pvBase, int nSize);
    void MemCopy(UInt32 uOffset, UInt32 uSize, void * pvBase);

    PageContainer() { m_pagesList.Initialize(); }
    ~PageContainer() { this->FreeAllPages(); }

    UInt32 GetTotalSize();
    ECode NestedAllocPages(PPage *const pInsertPos, UInt32 uNum,
                            UInt32 uOrder, UInt32 uNewOffset);

    ECode QueryPhysicalBlock(
        UInt32 uOffset, UInt32 *puPAddr, MemorySize *pSize);

public:
    DLinkNode       m_pagesList;
};

inline void PageContainer::MemZero()
{
    PPage * p;

    ForEachDLinkNode(PPage *, p, &m_pagesList) {
        p->MemZero();
    }
}

inline void PageContainer::MemCopy(void * pvBase, int nSize)
{
    PPage   *p;

    ForEachDLinkNode(PPage *, p, &m_pagesList) {
        nSize -= p->m_uSize;
        if (0 > nSize) {
            break;
        }

        p->MemCopy((void *)((UInt32)pvBase + p->m_uOffset));
    }

    if (nSize < 0) {
        nSize += p->m_uSize;
        memcpy(__VA(p->m_uAddr), (void *)((UInt32)pvBase + p->m_uOffset),
            nSize);
    }
}

inline void PageContainer::MemCopy(UInt32 uOffset, UInt32 uSize,
    void * pvBase)
{
    assert(uOffset + uSize >= uSize);
    PPage   *p;
    UInt32 n, m;

    ForEachDLinkNode(PPage *, p, &m_pagesList) {
        assert(p->m_uOffset + p->m_uSize >= p->m_uOffset);
        n = MAX(p->m_uOffset, uOffset);
        m = MIN(p->m_uOffset + p->m_uSize, uOffset + uSize);

        if (n < m) {
            memcpy(__VA(p->m_uAddr + n - p->m_uOffset),
                   (void *)((UInt32)pvBase + n - uOffset),
                   m - n);
        }

        if (uOffset + uSize <= p->m_uOffset) {
            break;
        }
    }
}

inline UInt32 PageContainer::GetTotalSize()
{
    register UInt32 size = 0;
    register PPage *p;

    ForEachDLinkNode(PPage *, p, &m_pagesList) {
        size += p->m_uSize;
    }
    return size;
}

#endif // __PPAGE_H__
